﻿using GeoSketch2.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Windows.Forms;
using System.Reflection;

namespace GeoSketch2.Archaeo
{
    /// <summary>
    /// A class to handle inputs outputs with files
    /// </summary>
    public static class FileIO
    {
#pragma warning disable 1591
        #region XMLElementsName

        public const string XML_ProjectLineTypeLibrary = "ProjectLineTypeLibrary";
        public const string XML_ProjectLayers = "ProjectLayers";
        public const string XML_ArchaeologistsList = "ArchaeologistsList";
        public const string XML_Project = "Project_GeoSketch2.Archaeo";
        public const string XML_ProjectSetup = "ProjectSetup";
        public const string XML_Layer0 = "Layer0";
        public const string XML_ExcavationAreas = "ExcavationAreas";
        public const string XML_ReferenceBenchmarks = "ReferenceBenchmarks";
        public const string XML_ArchaeoDrawableObjects = "ArchaeoDrawableObjects";
        public const string XML_SurveyLogBooks = "SurveyLogBooks";
        public const string XML_SoilSheetDescriptionModel = "SoilSheetDescriptionModel";

        #endregion

        #region DXFR13Consts
                public const string DXFR13_SquaresLayerName = "ExcavationSquares";
                public const string DXFR13_UnitsLayerName = "ExcavationUnits";
        #endregion
#pragma warning restore 1591

        #region Read/write a site to a file

        #region GSK File (XML format)

        #region Write
        /// <summary>
        /// Saves an excavationsite to a gsk file (Xml format)
        /// </summary>
        public static void WriteGSK(ArchaeoProject Project, string Filename)
        {
            XmlWriter Xw = Core.FileIO.GetXmlWriter(Filename);

            #region Document
            Xw.WriteStartDocument();
            Xw.WriteStartElement(XML_Project);

            #region Project Setup
            Xw.WriteStartElement(XML_ProjectSetup);

            #region LineTypeLibrary
            Xw.WriteStartElement(XML_ProjectLineTypeLibrary);
            foreach (LineType lt in Project.LineTypeLibrary)
            {
                lt.ToFile(Xw);
            }
            Xw.WriteEndElement();
            #endregion

            #region ProjectLayers
            Xw.WriteStartElement(FileIO.XML_ProjectLayers);
            foreach (Layer l in Project.ProjectLayers)
            {
                l.ToFile(Xw);
            }
            Xw.WriteEndElement();
            #endregion

            #region ArchaeologistsList
            Xw.WriteStartElement(FileIO.XML_ArchaeologistsList);
            foreach (string s in Project.ArchaeologistsList)
            {
                Xw.WriteStartElement(ExcavationArea.XML_Archaeologist);
                Xw.WriteString(s);
                Xw.WriteEndElement();
            }
            Xw.WriteEndElement();
            #endregion

            #region Layer0
            Xw.WriteStartElement(XML_Layer0);
            Xw.WriteString(Project.Layer0.Name);
            Xw.WriteEndElement();
            #endregion

            #region SoilSheetDescriptionModel
            Xw.WriteStartElement(BaseSoilSheet.XML_Template);
            Xw.WriteString(Project.SoilSheetModel.GetType().FullName);
            Xw.WriteEndElement();
            #endregion

            Xw.WriteEndElement();
            #endregion

            #region ExcavationAreas
            Xw.WriteStartElement(XML_ExcavationAreas);

            Project.MainSite.ToFile(Xw);
            
            Xw.WriteEndElement();
            #endregion

            #region Reference Benchmarks
            Xw.WriteStartElement(XML_ReferenceBenchmarks);
            foreach (Benchmark bm in Project.SiteBenchmarks)
            {
                if (bm.Reference)
                {
                    Xw.WriteStartElement(bm.GetType().FullName);

                    Xw.WriteStartElement(Benchmark.XML_Name);
                    Xw.WriteString(bm.Name);
                    Xw.WriteEndElement();

                    Xw.WriteStartElement(Benchmark.XML_Altitude);
                    Xw.WriteString(bm.Altitude.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));
                    Xw.WriteEndElement();

                    Xw.WriteEndElement();
                }
            }
            Xw.WriteEndElement();
            #endregion

            #region Archaeo Drawable Objects
            Xw.WriteStartElement(XML_ArchaeoDrawableObjects);
            foreach (BaseArchaeoDrawableObject obj in Project.ArchaeoObjects)
            {
                obj.ToFile(Xw);
            }
            Xw.WriteEndElement();
            #endregion

            #region Survey LogBooks
            List<SurveyLogBook> LogBooks = Project.SurveyLogBooks;

            Xw.WriteStartElement(XML_SurveyLogBooks);
            foreach (SurveyLogBook LogBook in LogBooks)
            {
                LogBook.ToFile(Xw);
            }
            Xw.WriteEndElement();
            #endregion

            Xw.WriteEndElement();
            Xw.WriteEndDocument();
            #endregion

            Xw.Close();
        } 
        #endregion

        #region Read
        /// <summary>
        /// Reads an excavationsite from a gsk file (Xml format)
        /// </summary>
        public static ArchaeoProject ReadGSK(string Filename)
        {
            XmlReader Xr = Core.FileIO.GetXmlReader(Filename);

            ArchaeoProject Project = null;

            try
            {
                #region Document
                Xr.ReadToFollowing(XML_Project);

                #region Project Setup
                Xr.ReadToFollowing(XML_ProjectSetup);

                #region LineTypeLibrary
                List<LineType> LineTypeLibrary = new List<LineType>();

                Xr.ReadToFollowing(XML_ProjectLineTypeLibrary);
                Xr.ReadToFollowing(typeof(LineType).FullName);
                while (Xr.NodeType != XmlNodeType.EndElement && Xr.Name != XML_ProjectLineTypeLibrary)
                {
                    LineTypeLibrary.Add(new LineType(Xr));
                    Xr.ReadEndElement();
                }
                #endregion

                #region ProjectLayers
                List<Layer> ProjectLayers = new List<Layer>();

                Xr.ReadToFollowing(XML_ProjectLayers);
                Xr.ReadToFollowing(typeof(Layer).FullName);
                while (Xr.NodeType != XmlNodeType.EndElement && Xr.Name != XML_ProjectLayers)
                {
                    ProjectLayers.Add(new Layer(Xr, LineTypeLibrary));
                    Xr.ReadEndElement();
                }
                #endregion

                #region Archaeologist List
                List<string> ArchaeologistList = new List<string>();

                Xr.ReadToFollowing(XML_ArchaeologistsList);
                if (!Xr.IsEmptyElement)
                {
                    Xr.ReadToFollowing(ExcavationArea.XML_Archaeologist);
                    while (Xr.NodeType != XmlNodeType.EndElement && Xr.Name != XML_ArchaeologistsList)
                    {
                        ArchaeologistList.Add(Xr.ReadString());
                        Xr.ReadEndElement();
                    }
                }
                #endregion

                #region Layer0
                Layer SiteLayer0;

                Xr.ReadToFollowing(XML_Layer0);
                string LayerName = Xr.ReadString();
                SiteLayer0 = ProjectLayers.Find(delegate(Layer l) { return LayerName == l.Name; });
                #endregion

                #region Soil Description Sheet Model
                BaseSoilSheet SoilSheetModel;

                Xr.ReadToFollowing(BaseSoilSheet.XML_Template);
                SoilSheetModel = (BaseSoilSheet)Type.GetType(Xr.ReadString()).InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] { });
                #endregion

                Project = new ArchaeoProject(LineTypeLibrary, ProjectLayers, ArchaeologistList, SiteLayer0, SoilSheetModel);
                Project.ProjectDirectory = Path.GetDirectoryName(Filename);

                #endregion

                #region ExcavationAreas
                Xr.ReadToFollowing(XML_ExcavationAreas);

                Xr.ReadToFollowing(typeof(ExcavationArea).FullName);

                new ExcavationArea(Xr, Project);

                #endregion

                #region Reference Benchmarks
                string BMName;
                double BMAltitude;
                Xr.ReadToFollowing(XML_ReferenceBenchmarks);
                if (!Xr.IsEmptyElement)
                {
                    Xr.ReadToFollowing(typeof(Benchmark).FullName);
                    while (!(Xr.NodeType == XmlNodeType.EndElement && Xr.Name == XML_ReferenceBenchmarks))
                    {
                        Xr.ReadToFollowing(Benchmark.XML_Name);
                        BMName = Xr.ReadString();

                        Xr.ReadToFollowing(Benchmark.XML_Altitude);
                        BMAltitude = Convert.ToDouble(Xr.ReadString(), Core.Vars.Setup.GeoSketch.NumberFormat);

                        Xr.ReadEndElement();

                        Xr.ReadEndElement();

                        Project.SiteBenchmarks.Add(new Benchmark(BMName, BMAltitude));
                    }
                }

                #endregion

                #region Archaeo Drawable Objects
                Xr.ReadToFollowing(XML_ArchaeoDrawableObjects);
                if (!Xr.IsEmptyElement)
                {
                    Xr.Read();

                    while (!(Xr.NodeType == XmlNodeType.EndElement && Xr.Name == XML_ArchaeoDrawableObjects))
                    {
                        Project.ArchaeoObjects.Add((BaseArchaeoDrawableObject)Type.GetType(Xr.Name).InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] { Xr, Project }));
                        Xr.ReadEndElement();
                    }
                }
                #endregion

                #region Survey LogBooks
                Xr.ReadToFollowing(XML_SurveyLogBooks);
                if (!Xr.IsEmptyElement)
                {
                    Xr.ReadToFollowing(typeof(SurveyLogBook).FullName);
                    while (!(Xr.NodeType == XmlNodeType.EndElement && Xr.Name == XML_SurveyLogBooks))
                    {
                        new SurveyLogBook(Xr, Project);
                        Xr.ReadEndElement();
                    }
                }
                #endregion

                #endregion

                Xr.Close();
            }
            catch
            {
                Project = null;
                MessageBox.Show("File corrupted");
            }
            return Project;
        }
        #endregion

        #endregion

        #region DXF R13 File (Autodesk DXF format R13)
        
        #region Write

        public static void WriteDXFR13(ArchaeoProject Project, string Filename)
        {
            Layer ExcavationAreasLayer = null;

            TextWriter Tw = Core.FileIO.GetTextWriter(Filename, false);

            //WriteDXFR13Header(Tw, Project);

            WriteDXFR13Tables(Tw, Project, out ExcavationAreasLayer);

            WriteDXFR13Entities(Tw, Project, ExcavationAreasLayer);

            Tw.WriteLine("0");
            Tw.WriteLine("EOF");

            Tw.Close();
        }

        #region Header

        static void WriteDXFR13Header(TextWriter Tw, ArchaeoProject Project)
        {
            WriteDXFR13NewSection(Tw, "HEADER");

            Tw.WriteLine("9");
            Tw.WriteLine("$ACADVER");
            Tw.WriteLine("1");
            Tw.WriteLine("AC1011");

            Tw.WriteLine("9");
            Tw.WriteLine("$CLAYER");
            Tw.WriteLine("8");
            Tw.WriteLine(Project.Layer0.Name);

            WriteDXFR13EndSection(Tw);
        } 

        #endregion

        #region Tables

        static void WriteDXFR13Tables(TextWriter Tw, ArchaeoProject Project, out Layer ExcavationAreasLayer)
        {
            LineType SolidLineType = null;

            WriteDXFR13NewSection(Tw, "TABLES");

            #region LineTypes
            WriteDXFR13NewTable(Tw, "LTYPE");

            Tw.WriteLine("70");
            Tw.WriteLine(Project.LineTypeLibrary.Count);

            foreach (LineType Lt in Project.LineTypeLibrary)
            {
                if (Lt.Style == LineStyle.Solid)
                {
                    SolidLineType = Lt;
                }
                Lt.ToDXFR13(Tw);
            }

            if (SolidLineType == null)
            {
                SolidLineType = new LineType(LineStyle.Solid);
                SolidLineType.ToDXFR13(Tw);
            }

            WriteDXFR13EndTable(Tw);
            #endregion

            #region Layers
            WriteDXFR13NewTable(Tw, "LAYER");

            Tw.WriteLine("70");
            Tw.WriteLine(Project.ProjectLayers.Count + 2);

            foreach (Layer l in Project.ProjectLayers)
            {
                l.ToDXFR13(Tw);
            }

            ExcavationAreasLayer = new Layer(DXFR13_SquaresLayerName, Vars.Setup.StandardObjects.ExcavationAreas.ExcavationSquare.DefaultLayerColor, SolidLineType, true);

            ExcavationAreasLayer.ToDXFR13(Tw);

            WriteDXFR13EndTable(Tw);
            #endregion

            WriteDXFR13EndSection(Tw);
        }

        static void WriteDXFR13NewTable(TextWriter Tw, string TableName)
        {
            Tw.WriteLine("0");
            Tw.WriteLine("TABLE");
            Tw.WriteLine("2");
            Tw.WriteLine(TableName);
        }

        static void WriteDXFR13EndTable(TextWriter Tw)
        {
            Tw.WriteLine("0");
            Tw.WriteLine("ENDTAB");
        }

        #endregion

        #region Entities

        static void WriteDXFR13Entities(TextWriter Tw, ArchaeoProject Project, Layer ExcavationAreasLayer)
        {
            WriteDXFR13NewSection(Tw, "ENTITIES");

            ExcavationAreaBrowseTree(Project.MainSite, ExcavationAreasLayer, Tw);

            foreach (BaseArchaeoDrawableObject Obj in Project.ArchaeoObjects)
            {
                Obj.ToDXFr13(Tw);
            }

            WriteDXFR13EndSection(Tw);
        }

        static void ExcavationAreaBrowseTree(ExcavationArea Area, Layer ExcavationAreasLayer, TextWriter Tw)
        {
            Polyline p = Area.BoundsPolyline;
            p.Layer = ExcavationAreasLayer;
            p.ToDXFr13(Tw);
            new InputText(Area.Centroid, Area.Name, ExcavationAreasLayer, null).ToDXFr13(Tw);

            foreach (ExcavationArea a in Area.ChildrenExcavationAreas)
            {
                ExcavationAreaBrowseTree(a, ExcavationAreasLayer,Tw);
            }
        }
        #endregion

        #region Misc

        static void WriteDXFR13NewSection(TextWriter Tw, string SectionName)
        {
            Tw.WriteLine("0");
            Tw.WriteLine("SECTION");
            Tw.WriteLine("2");
            Tw.WriteLine(SectionName);
        }

        static void WriteDXFR13EndSection(TextWriter Tw)
        {
            Tw.WriteLine("0");
            Tw.WriteLine("ENDSEC");
        }

        public static void WriteDXFR13TextCenter(TextWriter Tw, Point2D64D InsertionPoint, string Text, double Height)
        {
            Tw.WriteLine("10");
            Tw.WriteLine((InsertionPoint.X-Height).ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("20");
            Tw.WriteLine((InsertionPoint.Y-Height).ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("30");
            Tw.WriteLine("0.0");

            Tw.WriteLine("40");
            Tw.WriteLine(Height.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("1");
            Tw.WriteLine(Text);

            Tw.WriteLine("72");
            Tw.WriteLine("1");

            Tw.WriteLine("73");
            Tw.WriteLine("2");

            Tw.WriteLine("11");
            Tw.WriteLine(InsertionPoint.X.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("21");
            Tw.WriteLine(InsertionPoint.Y.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("31");
            Tw.WriteLine("0.0");
        }

        public static void WriteDXFR13TextLeftBottomAligned(TextWriter Tw, Point2D64D InsertionPoint, string Text, double Height)
        {
            Tw.WriteLine("10");
            Tw.WriteLine(InsertionPoint.X.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("20");
            Tw.WriteLine(InsertionPoint.Y.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("30");
            Tw.WriteLine("0.0");

            Tw.WriteLine("40");
            Tw.WriteLine(Height.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));

            Tw.WriteLine("1");
            Tw.WriteLine(Text);
        }

        #endregion

        #endregion

        #endregion

        #endregion
    }
}